﻿using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.EntityFrameworkCore.EFPlus;
using Abp.Extensions;
using Abp.ObjectMapping;
using Abp.Timing;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Core.WorkFlows.Enum;
using Mt.Site.Core.WorkFlows.Process.Entities;
using Mt.Site.Core.WorkFlows.Process.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace Mt.Site.Core.WorkFlows.Process.Stores
{
    internal class WorkFlowProcessStore: IWorkFlowProcessStore, ITransientDependency
    {
        public IRepository<WorkFlowProcess, Guid> WorkFlowProcessRepository => _workFlowProcessRepository;

        private readonly IRepository<WorkFlowProcess, Guid> _workFlowProcessRepository;
        private readonly IRepository<WorkFlowProcessMsg, long> _workFlowProcessMsgRepository;
        private readonly IRepository<WorkFlowRelationship, long> _workFlowRelationshipRepository;

        public IObjectMapper ObjectMapper { get; set; }

        public WorkFlowProcessStore(
            IRepository<WorkFlowProcess, Guid> workFlowProcessRepository,
            IRepository<WorkFlowProcessMsg, long> workFlowProcessMsgRepository,
            IRepository<WorkFlowRelationship, long> workFlowRelationshipRepository)
        {
            _workFlowProcessRepository = workFlowProcessRepository;
            _workFlowProcessMsgRepository = workFlowProcessMsgRepository;
            _workFlowRelationshipRepository = workFlowRelationshipRepository;

            ObjectMapper = NullObjectMapper.Instance;
        }

        public async Task<List<WorkFlowProcessDto>> GetAllProcessDataAsync(Guid paperId)
        {
            var list = await _workFlowProcessRepository.GetAllListAsync(c => c.PaperId == paperId && c.State != ActionState.Archive);
            return ObjectMapper.Map<List<WorkFlowProcessDto>>(list).OrderBy(c => c.StepCode).ThenBy(c => c.Code).ThenBy(c => c.CreationTime).ToList();
        }
        public async Task<List<WorkFlowProcessDto>> GetAllProcessDataWithArchiveAsync(Guid paperId)
        {
            var list = await _workFlowProcessRepository.GetAllListAsync(c => c.PaperId == paperId);
            return ObjectMapper.Map<List<WorkFlowProcessDto>>(list).OrderBy(c => c.StepCode).ThenBy(c => c.Code).ThenBy(c => c.CreationTime).ToList();
        }

        public async Task<List<WorkFlowProcessDto>> GetAllProcessDataAsync(Expression<Func<WorkFlowProcess, bool>> predicate)
        {
            var list = await _workFlowProcessRepository.GetAllListAsync(predicate);
            return ObjectMapper.Map<List<WorkFlowProcessDto>>(list);
        }

        public List<WorkFlowProcessDto> GetAllProcessData(Expression<Func<WorkFlowProcess, bool>> predicate)
        {
            var list = _workFlowProcessRepository.GetAllList(predicate);
            return ObjectMapper.Map<List<WorkFlowProcessDto>>(list).ToList();
        }

        public async Task UpdateAsync(WorkFlowProcessDto dto)
        {
            var entity = await _workFlowProcessRepository.GetAsync(dto.Id);
            ObjectMapper.Map(dto, entity);
            await _workFlowProcessRepository.UpdateAsync(entity);
        }

        public async Task InsertAsync(WorkFlowProcessDto dto)
        {
            var entity = ObjectMapper.Map<WorkFlowProcess>(dto);
            await _workFlowProcessRepository.InsertAsync(entity);
        }

        public async Task SaveReplyMsgAsync(Guid id, string msg, string customData, bool hasRead, bool isDefaultMsg)
        {
            var entity = await _workFlowProcessRepository.FirstOrDefaultAsync(c => c.Id == id && c.State == ActionState.Todo);
            if (entity != null)
            {
                entity.Reply = msg;
                entity.ReplyTime = Clock.Now;
                entity.IsDefaultMsg = isDefaultMsg;
                entity.CustomData = customData;
                entity.HasRead = hasRead;
                await _workFlowProcessRepository.UpdateAsync(entity);
            }
        }

        public async Task SetHasMarkedAsync(Guid id)
        {
            var entity = await _workFlowProcessRepository.FirstOrDefaultAsync(c => c.Id == id && c.State == ActionState.Todo);
            if (entity != null)
            {
                entity.HasMarked = true;
                entity.MarkedTime = Clock.Now;
                await _workFlowProcessRepository.UpdateAsync(entity);
            }
        }

        public async Task<bool> IsHas(Expression<Func<WorkFlowProcess, bool>> predicate)
        {
            int count = await _workFlowProcessRepository.CountAsync(predicate);
            return count > 0;
        }

        public async Task RemoveAsync(Expression<Func<WorkFlowProcess, bool>> predicate)
        {
            await _workFlowProcessRepository.BatchDeleteAsync(predicate);
        }

        public async Task<List<WorkFlowProcessDto>> GetAllOtherOperatorAsync(Guid paperId, long currentUserId) 
        {
            var query = from c in _workFlowProcessRepository.GetAll()
                        where c.PaperId == paperId && c.UserId != currentUserId && c.State == ActionState.Todo
                        select c;
            var list = await query.ToListAsync();

            return ObjectMapper.Map<List<WorkFlowProcessDto>>(list);
        }

        /// <inheritdoc/>
        public async Task<string> GetLastCodeAsync(Guid paperId, Guid? trunkId, int stepCode)
        {
            var children = await _workFlowProcessRepository.GetAllListAsync(c => c.PaperId == paperId && c.TrunkId == trunkId && c.StepCode == stepCode);
            var lastChild = children.OrderBy(c => c.Code).LastOrDefault();
            return lastChild?.Code;
        }

        public async Task TryAddAdditionalMsg(string msg, WorkFlowProcessDto current)
        {
            if (!msg.IsNullOrWhiteSpace())
            {
                var newMsg = new WorkFlowProcessMsg
                {
                    PaperId = current.PaperId,
                    TaskId = current.Id,
                    Reply = msg,
                    ReplyTime = Clock.Now,
                    UserName = current.UserName
                };
                await _workFlowProcessMsgRepository.InsertAsync(newMsg);
            }
        }

        public async Task<List<WorkFlowProcessMsgDto>> GetAllAdditionalMsgAsync(Guid paperId)
        {
            var list = await _workFlowProcessMsgRepository.GetAllListAsync(c => c.PaperId == paperId);
            return ObjectMapper.Map<List<WorkFlowProcessMsgDto>>(list).OrderBy(c => c.ReplyTime).ToList();
        }

        public async Task<List<WorkFlowRelationshipDto>> GetWorkFlowRelationshipsAsync(Guid paperId)
        {
            var list = await _workFlowRelationshipRepository.GetAllListAsync(c => c.PaperId == paperId && c.IsCanceled == false);
            return ObjectMapper.Map<List<WorkFlowRelationshipDto>>(list);
        }
        public async Task<List<WorkFlowRelationshipDto>> GetWorkFlowRelationshipsWithCanceledAAsync(Guid paperId)
        {
            var list = await _workFlowRelationshipRepository.GetAllListAsync(c => c.PaperId == paperId);
            return ObjectMapper.Map<List<WorkFlowRelationshipDto>>(list);
        }

        public List<WorkFlowRelationshipDto> GetWorkFlowRelationships(Expression<Func<WorkFlowRelationship, bool>> predicate)
        {
            var list = _workFlowRelationshipRepository.GetAllList(predicate);
            return ObjectMapper.Map<List<WorkFlowRelationshipDto>>(list);
        }

        public async Task SetRelationshipHasMarkedAsync(Guid paperId, Guid TaskId)
        {
            var list = await _workFlowRelationshipRepository.GetAllListAsync(
                c => c.PaperId == paperId && c.TaskId == TaskId && c.IsCanceled == false && c.HasMarked == false);
            foreach (var item in list)
            {
                item.HasMarked = true;
            }
        }

        public List<WorkFlowToDoTaskCount> GetAllToDoTaskCount()
        {
            var query = from c in _workFlowProcessRepository.GetAll()
                        where c.State == ActionState.Todo && c.StepCode > 1
                        group c by new { c.UserId, c.WorkFlowId } into g
                        select new WorkFlowToDoTaskCount
                        {
                            UserId = g.Key.UserId,
                            WorkFlowId = g.Key.WorkFlowId,
                            Count = g.Count()
                        };

            return query.ToList();
        }
    }
}
